﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Stonecode.Solar.ParticleEngine.ParticleSystems;
using Stonecode.Solar.ParticleEngine.Utils;
using Microsoft.Xna.Framework.Graphics;

namespace Stonecode.Solar.ParticleEngine.Emitters
{
    /// <summary>
    /// Creates a rectangle with a center reference point
    /// </summary>
    public struct RectangleF
    {
        public Vector2 Position;
        public Vector2 Size;

        public float HalfWidth;
        public float HalfHeight;

        public float X { get { return Position.X; } set { Position.X = value; } }
        public float Y { get { return Position.Y; } set { Position.Y = value; } }
        public float Z { get; set; }
        public float Width 
        { 
            get { return Size.X; } 
            set 
            { 
                Size.X = value;
                HalfWidth = value * 0.5f;
            } 
        }
        public float Height 
        { 
            get { return Size.Y; } 
            set 
            { 
                Size.Y = value;
                HalfHeight = value * 0.5f;
            } 
        }

        public RectangleF(Vector2 pos, float posZ, Vector2 size)
        {
            this = new RectangleF();
            Position = pos;
            Size = size;
            HalfWidth = Size.X * 0.5f;
            HalfHeight = Size.Y * 0.5f;
            Z = posZ;
            
        }
    }

    public class RectangleEmitter : Emitter
    {
        
        public RectangleEmitterTypeSettings EmitterTypeSettings;
        public bool RotateVelocity = false;

        private Vector2 _velocityHelper;
        private float _floatHelper;

        public RectangleEmitter(GraphicsDevice graphics, Effect pEffect, EmitterSettings pSettings,
            Texture2D texture, float particlesPerSecond, RectangleF rec, float rot, Matrix view, Matrix proj)
            : base(graphics, pEffect, pSettings, texture, particlesPerSecond, view, proj, EmitterType.Rectangle)
        {
            EmitterTypeSettings = new RectangleEmitterTypeSettings(rec, rot, true);
            _velocityHelper = Vector2.Zero;
            //Rectangle = rec;
            //Rotation = rot;
        }

        public override void Dispose()
        {
            EmitterTypeSettings = null;
            base.Dispose();
        }

        private void GetPointInRectangle(out Vector2 p, out Vector2 v)
        {
            p = Vector2.Zero;
            NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfWidth, EmitterTypeSettings.Rectangle.HalfWidth, out p.X);
            NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfHeight, EmitterTypeSettings.Rectangle.HalfHeight, out p.Y);
            NumberGenerator.GenerateRandomVector2(new Vector2(_particleSystem.Settings.MinHorizontalVelocity, _particleSystem.Settings.MinVerticalVelocity),
                new Vector2(_particleSystem.Settings.MaxHorizontalVelocity, _particleSystem.Settings.MaxVerticalVelocity), out v);

            if (EmitterTypeSettings.Rotation != 0)
            {
                Vector2.Transform(ref p, ref EmitterTypeSettings.mRot, out p);
                

                if (RotateVelocity)
                    Vector2.Transform(ref v, ref EmitterTypeSettings.mRot, out v);
            }

            p += EmitterTypeSettings.Rectangle.Position;
        }

        private void GetPointOnRectangle(out Vector2 p, out Vector2 v)
        {
            p = Vector2.Zero;
            
            
            int side;
            NumberGenerator.GenerateRandomInteger(1, 5, out side);

            switch (side)
            {
                case 1:
                    {
                        NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfWidth, 
                            EmitterTypeSettings.Rectangle.HalfWidth, out p.X);
                        p.Y = EmitterTypeSettings.Rectangle.HalfHeight;
                        break;
                    }
                case 2:
                    {
                        NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfHeight,
                            EmitterTypeSettings.Rectangle.HalfHeight, out p.Y);
                        p.X = EmitterTypeSettings.Rectangle.HalfWidth;
                        break;
                    }
                case 3:
                    {
                        NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfWidth,
                            EmitterTypeSettings.Rectangle.HalfWidth, out p.X);
                        p.Y = -EmitterTypeSettings.Rectangle.HalfHeight;
                        break;
                    }
                case 4:
                    {
                        NumberGenerator.GenerateRandomFloat(-EmitterTypeSettings.Rectangle.HalfHeight,
                            EmitterTypeSettings.Rectangle.HalfHeight, out p.Y);
                        p.X = -EmitterTypeSettings.Rectangle.HalfWidth;
                        break;
                    }
            }

            // Generate Velocity
            

            // If this particle should only radiate outward, then apply that calulation
            if (EmitterTypeSettings.RadiateOutward)
            {
                NumberGenerator.GenerateRandomFloat((float)Math.Min(_particleSystem.Settings.MinHorizontalVelocity, _particleSystem.Settings.MinVerticalVelocity),
                   (float)Math.Max(_particleSystem.Settings.MaxHorizontalVelocity, _particleSystem.Settings.MaxVerticalVelocity), out _floatHelper);

                _floatHelper = Math.Abs(_floatHelper);

                Vector2.Normalize(ref p, out _velocityHelper);

                Vector2.Multiply(ref _velocityHelper, _floatHelper, out v);

                if (p.X < 0 && v.X > 0 || p.X > 0 && v.X < 0 || p.Y < 0 && v.Y > 0 || p.Y > 0 && v.Y < 0)
                    throw new Exception("Crap velocity!!!");
            }
            else
            {
                NumberGenerator.GenerateRandomVector2(_particleSystem.Settings.MinVelocity, _particleSystem.Settings.MaxVelocity, out v);
            }
           

            if (EmitterTypeSettings.Rotation != 0)
            {
                Vector2.Transform(ref p, ref EmitterTypeSettings.mRot, out p);
                if (RotateVelocity)
                    Vector2.Transform(ref v, ref EmitterTypeSettings.mRot, out v);
            }

            p += EmitterTypeSettings.Rectangle.Position;


        }

        private void SetRectanglePosition()
        {
            RectangleF rec = EmitterTypeSettings.Rectangle;
            rec.X = base.Position.X;
            rec.Y = base.Position.Y;
            EmitterTypeSettings.Rectangle = rec;
        }

        public override void Update(float dt)
        {
            SetRectanglePosition();
            //this.Position = new Vector2(base.Position.X, base.Position.Y); 
            if (IsAlive && Enabled && AutoGenerate)
            {
                // Work out how much time has passed since the previous update.
                //float elapsedTime = dt; // (float)gameTime.ElapsedGameTime.TotalSeconds;

                if (dt > 0)
                {
                    // If we had any time left over that we didn't use during the
                    // previous update, add that to the current elapsed time.
                    float timeToSpend = timeLeftOver + dt;

                    // Counter for looping over the time interval.
                    float currentTime = -timeLeftOver;

                    // Create particles as long as we have a big enough time interval.
                    while (timeToSpend > timeBetweenParticles)
                    {
                        currentTime += timeBetweenParticles;
                        timeToSpend -= timeBetweenParticles;

                        Vector2 position;
                        Vector2 velocity;
                        if (EmitterTypeSettings.CreateOnEdge)
                            GetPointOnRectangle(out position, out velocity);
                        else
                            GetPointInRectangle(out position, out velocity);
                        //GetPointOnLine();

                        // Create the particle.
                        //_particleSystem.AddParticle(new Vector3(position, EmitterTypeSettings.Rectangle.Z), new Vector3(velocity, 0));
                        _particleSystem.AddParticleManualVelocity(new Vector3(position, EmitterTypeSettings.Rectangle.Z),
                            new Vector3(velocity, 0));
                    }

                    // Store any time we didn't use, so it can be part of the next update.
                    timeLeftOver = timeToSpend;
                }
            }

            
            base.Update(dt);
        }

        public override void Trigger(float dt)
        {
            if (Position.X != EmitterTypeSettings.Rectangle.X ||
                Position.Y != EmitterTypeSettings.Rectangle.Y)
                SetRectanglePosition();

            float timeToSpend = timeLeftOver + dt;

            // Counter for looping over the time interval.
            float currentTime = -timeLeftOver;

            // Create particles as long as we have a big enough time interval.
            while (timeToSpend > timeBetweenParticles)
            {
                currentTime += timeBetweenParticles;
                timeToSpend -= timeBetweenParticles;

                Vector2 position;
                Vector2 velocity;
                if (EmitterTypeSettings.CreateOnEdge)
                    GetPointOnRectangle(out position, out velocity);
                else
                    GetPointInRectangle(out position, out velocity);
                
                // Create the particle.
                _particleSystem.AddParticleManualVelocity(new Vector3(position, EmitterTypeSettings.Rectangle.Z), new Vector3(velocity, 0));
            }

            timeLeftOver = timeToSpend;


        }

        public override void Trigger(int particleCount)
        {
            if (Position.X != EmitterTypeSettings.Rectangle.X ||
                Position.Y != EmitterTypeSettings.Rectangle.Y)
                SetRectanglePosition();

            for (int i = 0; i < particleCount; i++)
            {
                Vector2 position;
                Vector2 velocity;
                if (EmitterTypeSettings.CreateOnEdge)
                    GetPointOnRectangle(out position, out velocity);
                else
                    GetPointInRectangle(out position, out velocity);
                //GetPointOnLine();

                // Create the particle.
                _particleSystem.AddParticleManualVelocity(new Vector3(position, EmitterTypeSettings.Rectangle.Z), new Vector3(velocity, 0));
            }
        }

        public override void TriggerWithOffset(float dt, Vector2 offset)
        {
            float timeToSpend = timeLeftOver + dt;

            // Counter for looping over the time interval.
            float currentTime = -timeLeftOver;

            // Create particles as long as we have a big enough time interval.
            while (timeToSpend > timeBetweenParticles)
            {
                currentTime += timeBetweenParticles;
                timeToSpend -= timeBetweenParticles;

                Vector2 position;
                Vector2 velocity;
                if (EmitterTypeSettings.CreateOnEdge)
                    GetPointOnRectangle(out position, out velocity);
                else
                    GetPointInRectangle(out position, out velocity);
                Vector2.Add(ref position, ref offset, out position);
                //GetPointOnLine();

                // Create the particle.
                _particleSystem.AddParticleManualVelocity(new Vector3(position, EmitterTypeSettings.Rectangle.Z), new Vector3(velocity, 0));
            }

            timeLeftOver = timeToSpend;

        }



    }
}
